/**
 * 
 */
package com.manthano.applogin.authorization.utils;

import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import com.manthano.applogin.authorization.exception.InvalidUserException;
import com.manthano.applogin.management.bo.AppUser;
import com.manthano.applogin.management.bo.LoginUser;
import com.manthano.applogin.management.exception.UserException;
import com.manthano.applogin.management.service.AppUserService;
import com.manthano.util.context.Context;

/**
 * @author Dell
 */
public class AuthorizationManager implements UserDetailsService
{
	AuthenticationManager	authenticationManager	= null;

	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
	{
		AppUserService appUserService = (AppUserService) Context.getBean("appUserService");

		org.springframework.security.core.userdetails.User authUser = null;

		/*
		 * Spring definations accountNonExpired - set to true if the account has
		 * not expired credentials. NonExpired - set to true if the credentials
		 * have not expired. accountNonLocked - set to true if the account is
		 * not locked
		 */
		try
		{
			LoginUser loginUser = appUserService.getUserByUserName(username);
			AppUser appUser = loginUser.getAppUser();
			if (loginUser != null)
			{
				authUser = new org.springframework.security.core.userdetails.User(appUser.getUserName(),
						appUser.getPasswordHash(), appUser.isActive(), !appUser.isExpired(), true, true,
						loginUser.getRoles());
			}
		}
		catch (UserException e)
		{
			throw new UsernameNotFoundException("LoginUser not Found");
		}
		return authUser;
	}

	/**
	 * @param userId
	 * @return Boolean
	 * @throws InvalidUserException
	 */
	public Boolean isLoginSuccesfull(String userId) throws InvalidUserException
	{
		boolean isLoginSuccesfull = false;
		AppUserService appUserService = (AppUserService) Context.getBean("userService");
		try
		{
			LoginUser loginUser = appUserService.getUserById(userId);
			AppUser appUser = loginUser.getAppUser();
			UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
					appUser.getEmailAddress(), appUser.getPasswordHash(), loginUser.getRoles());

			Authentication authenticatedUser = authenticationManager.authenticate(token);
			SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
			isLoginSuccesfull = true;
		}
		catch (UserException e)
		{
			throw new InvalidUserException("LoginUser not present in Database", e);
		}
		catch (AuthenticationException e)
		{
			throw new InvalidUserException("LoginUser not authenticated", e);
		}
		return isLoginSuccesfull;
	}

	/**
	 * @param username
	 * @param password
	 * @return boolean
	 * @throws InvalidUserException
	 */
	public boolean loginUser(String username, String password) throws InvalidUserException
	{
		Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
				username, password));
		boolean authenticated = isAuthenticated(authentication);
		if (authenticated)
		{
			SecurityContextHolder.getContext().setAuthentication(authentication);
		}
		return authenticated;
	}

	private boolean isAuthenticated(Authentication authentication)
	{
		return authentication != null && !(authentication instanceof AnonymousAuthenticationToken)
				&& authentication.isAuthenticated();
	}

	/**
	 * @return boolean
	 * @throws InvalidUserException
	 */
	public boolean isLoggedIn() throws InvalidUserException
	{
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		return isAuthenticated(authentication);
	}

	/**
	 * @return LoginUser
	 * @throws InvalidUserException
	 */
	public LoginUser getLoggedUser() throws InvalidUserException
	{
		LoginUser loggedUser = null;
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		if (isAuthenticated(authentication))
		{
			Object principal = authentication.getPrincipal();
			if (principal instanceof LoginUser)
			{
				loggedUser = ((LoginUser) principal);
			}
			else
			{
				throw new InvalidUserException(
						"Expected class of authentication principal is AuthenticationUserDetails. Given: "
								+ principal.getClass());
			}
		}
		return loggedUser;
	}
}
